package cn.edu.zju.om.analyse;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

public class KMeansCluster {
	
	private int clusterCount;
	
	private int loopBound;
	
	private final double THRESHOLD = -0.60;
	
	private List<PhraseFeatureVector> phrases;
	
	private List<PhraseGroup> phraseGroups;
	
	private Map<PhraseFeatureVector, PhraseGroup> clusteredTo;
	
	public KMeansCluster(List<PhraseFeatureVector> phrases, int clusterCount, int loopBound) {
		this.phrases = phrases;
		this.clusterCount = clusterCount;
		this.loopBound = loopBound;
		
		this.phraseGroups = new ArrayList<PhraseGroup>();
		this.clusteredTo = new HashMap<PhraseFeatureVector, PhraseGroup>();
	}
	
	public List<PhraseGroup> caculateClusters() throws Exception {
		init();
		
		// init is the first loop
		for(int i = 1; i < loopBound; ++i) {
			for(PhraseFeatureVector phrase : phrases) {
				double minDistance = Double.MAX_VALUE;
				PhraseGroup assignToGroup = null;
				
				for(PhraseGroup group : phraseGroups) {
					double distance = group.caculateDistance(phrase);
					
					System.out.println(distance);
					
					if (distance < minDistance && distance < THRESHOLD) {
						minDistance = distance;
						assignToGroup = group;
					}
				}
				
				PhraseGroup assignedGroup = clusteredTo.get(phrase);
				
				if(assignToGroup != assignedGroup && assignToGroup != null && assignedGroup != null) {
					assignToGroup.addToGroup(phrase);
					assignedGroup.removeFromGroup(phrase);
					
					clusteredTo.put(phrase, assignToGroup);
				}
			}
			
			// check if there is anything changed
			boolean isChanged = false;
			for(PhraseGroup group : phraseGroups) {
				if(group.isChanged()) {
					isChanged = true;
				}
				
				group.updateGroupCenter();
			}
			
			// The cluster reached stable state
			if(!isChanged) {
				System.out.println(String.format("Kmeans stopped after %d iteration.", i));
				break;
			}
		}
		
		return phraseGroups;
	}

	private void init() throws Exception {
		//Collections.shuffle(phrases);
		
		Helper.assertFeatureVectors(phrases);
		Collections.sort(phrases, new Comparator<PhraseFeatureVector>() {
			public int compare(PhraseFeatureVector o1, PhraseFeatureVector o2) {
				return o2.getPhrase().getCount() - o1.getPhrase().getCount();
			}
		});
		
		Helper.assertFeatureVectors(phrases);
		
		for(int i = 0 ; i < clusterCount; ++i) {
			PhraseFeatureVector phrase = phrases.get(i);
			PhraseGroup group = new PhraseGroup(phrase);
			
			phraseGroups.add(group);
			clusteredTo.put(phrase, group);
		}
		
		Helper.assertFeatureVectors(phrases);
		
		for(int i = clusterCount; i < phrases.size(); ++i) {
			PhraseFeatureVector phrase = phrases.get(i);
			
			double minDistance = Double.MAX_VALUE;
			PhraseGroup assignedGroup = null;
			
			for(PhraseGroup group : phraseGroups) {
				double distance = group.caculateDistance(phrase);
				
				if(distance < minDistance) {
					minDistance = distance;
					assignedGroup = group;
				}
			}
			
			if (minDistance < THRESHOLD) {
				assignedGroup.addToGroup(phrase);
				clusteredTo.put(phrase, assignedGroup);
			}
		}
		
		Helper.assertFeatureVectors(phrases);
		
		for(PhraseGroup group : phraseGroups) {
			group.updateGroupCenter();
		}
		
		Helper.assertFeatureVectors(phrases);
	}

}
